package com.taobao.diamond.server.service;

import com.taobao.diamond.domain.ConfigInfo;
import com.taobao.diamond.domain.Page;
import com.taobao.diamond.server.utils.PaginationHelper;
import com.taobao.diamond.utils.ResourceUtils;
import com.taobao.diamond.utils.TimeUtils;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Properties;
import javax.annotation.PostConstruct;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.stereotype.Service;

@Service
public class PersistService
{
    private static final String JDBC_DRIVER_NAME = "com.mysql.jdbc.Driver";
    private static final int MAX_ROWS = 10000;
    private static final int QUERY_TIMEOUT = 2;
    private static final ConfigInfoRowMapper CONFIG_INFO_ROW_MAPPER = new ConfigInfoRowMapper();
    private JdbcTemplate jt;

    private static String ensurePropValueNotNull(String srcValue)
    {
        if (srcValue == null) {
            throw new IllegalArgumentException("property is illegal:" + srcValue);
        }

        return srcValue;
    }

    public JdbcTemplate getJdbcTemplate()
    {
        return this.jt;
    }

    @PostConstruct
    public void initDataSource()
            throws Exception
    {
        Properties props = ResourceUtils.getResourceAsProperties("config.properties");
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl(ensurePropValueNotNull(props.getProperty("db.url")));
        ds.setUsername(ensurePropValueNotNull(props.getProperty("db.user")));
        ds.setPassword(ensurePropValueNotNull(props.getProperty("db.password")));
        ds.setInitialSize(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.initialSize"))));
        ds.setMaxActive(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.maxActive"))));
        ds.setMaxIdle(Integer.parseInt(ensurePropValueNotNull(props.getProperty("db.maxIdle"))));
        ds.setMaxWait(Long.parseLong(ensurePropValueNotNull(props.getProperty("db.maxWait"))));
        ds.setPoolPreparedStatements(Boolean.parseBoolean(ensurePropValueNotNull(props.getProperty("db.poolPreparedStatements"))));

        this.jt = new JdbcTemplate();
        this.jt.setDataSource(ds);

        this.jt.setMaxRows(10000);

        this.jt.setQueryTimeout(2);
    }

    public void addConfigInfo(final ConfigInfo configInfo)
    {
        final Timestamp time = TimeUtils.getCurrentTime();

        this.jt.update("insert into config_info (data_id,group_id,content,md5,gmt_create,gmt_modified) values(?,?,?,?,?,?)", new PreparedStatementSetter()
        {
            public void setValues(PreparedStatement ps) throws SQLException
            {
                int index = 1;
                ps.setString(index++, configInfo.getDataId());
                ps.setString(index++, configInfo.getGroup());
                ps.setString(index++, configInfo.getContent());
                ps.setString(index++, configInfo.getMd5());
                ps.setTimestamp(index++, time);
                ps.setTimestamp(index++, time);
            }
        });
    }

    public void removeConfigInfo(final ConfigInfo configInfo)
    {
        this.jt.update("delete from config_info where data_id=? and group_id=?", new PreparedStatementSetter() {
            public void setValues(PreparedStatement ps) throws SQLException {
                int index = 1;
                ps.setString(index++, configInfo.getDataId());
                ps.setString(index++, configInfo.getGroup());
            }
        });
    }

    public void updateConfigInfo(final ConfigInfo configInfo)
    {
        final Timestamp time = TimeUtils.getCurrentTime();

        this.jt.update("update config_info set content=?,md5=?,gmt_modified=? where data_id=? and group_id=?", new PreparedStatementSetter()
        {
            public void setValues(PreparedStatement ps) throws SQLException
            {
                int index = 1;
                ps.setString(index++, configInfo.getContent());
                ps.setString(index++, configInfo.getMd5());
                ps.setTimestamp(index++, time);
                ps.setString(index++, configInfo.getDataId());
                ps.setString(index++, configInfo.getGroup());
            }
        });
    }

    public ConfigInfo findConfigInfo(String dataId, String group)
    {
        try {
            return (ConfigInfo)this.jt.queryForObject("select id,data_id,group_id,content,md5 from config_info where data_id=? and group_id=?", new Object[] { dataId, group }, CONFIG_INFO_ROW_MAPPER);
        }
        catch (EmptyResultDataAccessException e)
        {
        }

        return null;
    }

    public ConfigInfo findConfigInfo(long id)
    {
        try
        {
            return (ConfigInfo)this.jt.queryForObject("select id,data_id,group_id,content,md5 from config_info where id=?", new Object[] { Long.valueOf(id) }, CONFIG_INFO_ROW_MAPPER);
        }
        catch (EmptyResultDataAccessException e) {
        }
        return null;
    }

    public Page<ConfigInfo> findConfigInfoByDataId(int pageNo, int pageSize, String dataId)
    {
        PaginationHelper helper = new PaginationHelper();
        return helper.fetchPage(this.jt, "select count(id) from config_info where data_id=?", "select id,data_id,group_id,content,md5 from config_info where data_id=?", new Object[] { dataId }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER);
    }

    public Page<ConfigInfo> findConfigInfoByGroup(int pageNo, int pageSize, String group)
    {
        PaginationHelper helper = new PaginationHelper();
        return helper.fetchPage(this.jt, "select count(id) from config_info where group_id=?", "select id,data_id,group_id,content,md5 from config_info where group_id=?", new Object[] { group }, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER);
    }

    public Page<ConfigInfo> findAllConfigInfo(int pageNo, int pageSize)
    {
        PaginationHelper helper = new PaginationHelper();
        return helper.fetchPage(this.jt, "select count(id) from config_info order by id", "select id,data_id,group_id,content,md5 from config_info order by id ", new Object[0], pageNo, pageSize, CONFIG_INFO_ROW_MAPPER);
    }

    public Page<ConfigInfo> findConfigInfoLike(int pageNo, int pageSize, String dataId, String group)
    {
        if ((StringUtils.isBlank(dataId)) && (StringUtils.isBlank(group))) {
            return findAllConfigInfo(pageNo, pageSize);
        }

        PaginationHelper helper = new PaginationHelper();

        String sqlCountRows = "select count(id) from config_info where ";
        String sqlFetchRows = "select id,data_id,group_id,content,md5 from config_info where ";
        boolean wasFirst = true;
        if (!StringUtils.isBlank(dataId)) {
            sqlCountRows = sqlCountRows + "data_id like ? ";
            sqlFetchRows = sqlFetchRows + "data_id like ? ";
            wasFirst = false;
        }
        if (!StringUtils.isBlank(group)) {
            if (wasFirst) {
                sqlCountRows = sqlCountRows + "group_id like ? ";
                sqlFetchRows = sqlFetchRows + "group_id like ? ";
            }
            else {
                sqlCountRows = sqlCountRows + "and group_id like ? ";
                sqlFetchRows = sqlFetchRows + "and group_id like ? ";
            }
        }

        Object[] args = null;
        if ((!StringUtils.isBlank(dataId)) && (!StringUtils.isBlank(group))) {
            args = new Object[] { generateLikeArgument(dataId), generateLikeArgument(group) };
        }
        else if (!StringUtils.isBlank(dataId)) {
            args = new Object[] { generateLikeArgument(dataId) };
        }
        else if (!StringUtils.isBlank(group)) {
            args = new Object[] { generateLikeArgument(group) };
        }

        return helper.fetchPage(this.jt, sqlCountRows, sqlFetchRows, args, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER);
    }

    private String generateLikeArgument(String s)
    {
        if (s.indexOf("*") >= 0) {
            return s.replaceAll("\\*", "%");
        }
        return "%" + s + "%";
    }

    private static final class ConfigInfoRowMapper
            implements ParameterizedRowMapper<ConfigInfo>
    {
        public ConfigInfo mapRow(ResultSet rs, int rowNum)
                throws SQLException
        {
            ConfigInfo info = new ConfigInfo();
            info.setId(rs.getLong("id"));
            info.setDataId(rs.getString("data_id"));
            info.setGroup(rs.getString("group_id"));
            info.setContent(rs.getString("content"));
            info.setMd5(rs.getString("md5"));
            return info;
        }
    }
}